#!/usr/bin/env python3
# -*- coding:UTF-8 -*-
import argparse
import json
import datetime, time
import sys

import requests

# Gitlab鉴权HEADER
headers = {'PRIVATE-TOKEN': 'Kc5hh-ua6Vyua5s6byMU'}
headers_micode = {'PRIVATE-TOKEN': 'sWo2sor-pdMCRXurPBR9'}

# Sonar鉴权HEADER
headers_auth = {'AUTHORIZATION': 'Basic NjE2MDk3OGUxNWY0YzE1OGFiNWM1NzA1ZTI2NmNlNjFlN2VjOWFiZjo='}

format_url_prefix = "https://git.n.xiaomi.com/api/v4/"
format_url_micode_prefix = "https://micode.be.xiaomi.com/api/v4/"
format_url_group_project = "groups/%s/projects"
format_url_pipeline_subfix = "projects/%s/jobs"
format_url_mr_subfix = "projects/%s/merge_requests"
format_url_comment_subfix = "projects/%s/merge_requests/%s/notes"
format_url_commit_subfix = "projects/%s/merge_requests/%s/commits"
format_url_repository_commit = 'projects/%s/repository/commits'

format_sonar_url_prefix = "https://sonarqube.mioffice.cn/api/"
format_sonar_url_subfix = "measures/search"

# pipeline stage规范
pipeline_stage_rule = set(['build', 'checkstyle', 'sonar', 'test'])

# 待检查组集合
group_set_gitn = set([
    # nr-xmstore
    '15365',
    # nrme
    '13197'
])

group_set_micode = set([
    # 服务端
    '476', '469', '3188',
    # 客户端
    '2816', '4125', '3135',
    # 前端
    '3254', '2888', '215', '4213',
    # pc
    '1891'
])

project_name_id_map = dict()

# 项目所需的token map, prefix map, name group map
project_id_prefix_map = dict()
project_id_token_map = dict()
project_name_group_map = dict()

# 统计结果
project_result = dict()

# 判断项目活跃的repository commit阈值,即k值
commit_threshold = 5

# 命令行参数解析
parser = argparse.ArgumentParser(description='manual to this script')
parser.add_argument("--projectId", type=str, default="80093")
parser.add_argument("--projectName", type=str, default="milog")
parser.add_argument("--groupId", type=str, default="23581")
parser.add_argument("--gitlabType", type=str, default="git_n")
parser.add_argument("--gitlabToken", type=str, default="pP2CZruiWa5Nm1C4g835")


# 获取group下的所有项目
def request_group_project_info(request_url, group_id, headers):
    url = request_url % group_id
    # print("=================================url: ", url)

    try:
        r = requests.get(url=url, headers=headers, params={'simple': 'true', 'per_page': 200, 'page': 1}, timeout=3000)
        print(r.text)
    except BaseException as e:
        print("request_group_project_info ex: ", e)
    else:
        # print(r.text)
        return r


# 分析group下的所有项目
def analyze_group_project_info(group_project_info, headers, url_prefix):
    # print("=================================group_project_info: ", group_project_info)
    json_data = json.loads(group_project_info.text)
    list_len = len(json_data)

    for index in range(list_len):
        ele = json_data[index]
        # print(json.dumps(ele))

        if ele["name"] not in project_name_id_map:
            project_name_id_map[ele["name"]] = ele["id"]

            project_id_token_map[ele["id"]] = headers
            project_id_prefix_map[ele["id"]] = url_prefix

            # sonar的参数是group:project_name
            name_group_str = ele["name_with_namespace"]
            project_name_group_map[ele["name"]] = name_group_str.split('/')[0].strip()

    return list_len


# 获取pipeline信息
def request_pipeline_info(request_url, project_id):
    url = request_url % project_id
    # print("=================================url: ", url)

    try:
        r = requests.get(url=url, headers=project_id_token_map[project_id], params={'scope': 'success'}, timeout=3000)
        # print(r.text)
    except BaseException as e:
        print("request_pipeline_info ex: ", e)
    else:
        # print(r.text)
        return r


# 分析pipeline信息
def analyze_pipeline_info(pipeline_info, stage_set):
    # print("=================================pipeline_info: ", pipeline_info)
    json_data = json.loads(pipeline_info.text)
    list_len = len(json_data)

    for index in range(list_len):
        ele = json_data[index]
        # print(json.dumps(ele))

        stage_set.add(ele['stage'])

    return stage_set == pipeline_stage_rule


# 获取merge request信息
def request_merge_request_info(request_url, project_id, page_no, created_after):
    url = request_url % project_id
    # print("=================================url: ", url)

    try:
        r = requests.get(url=url, headers=project_id_token_map[project_id],
                         params={'state': 'merged', 'target_branch': 'master', 'order_by': 'created_at', 'sort': 'desc',
                                 'per_page': '100', 'page': page_no, 'created_after': created_after},
                         timeout=3000)
        # print(r.text)
    except BaseException as e:
        print("request_merge_request_info ex: ", e)
    else:
        # print(r.text)
        return r


# 分析merge request信息
def analyze_merge_request_info(merge_request_info, project_name, project_id):
    # print("=================================merge_request_info: ", merge_request_info)
    json_data = json.loads(merge_request_info.text)
    list_len = len(json_data)
    # print("merge request info len: " + str(list_len))

    for index in range(list_len):
        ele = json_data[index]
        # print(json.dumps(ele))

        mr_id = ele['iid']
        # print("analyze_merge_request_info, mr_id: " + str(mr_id))
        try:
            if 'mr_id' not in project_result[project_name]:
                project_result[project_name]['mr_id'] = dict()
            if 'mr_comment_count' not in project_result[project_name]['mr_id']:
                project_result[project_name]['mr_id']['mr_comment_count'] = dict()
            if 'good_commit_count' not in project_result[project_name]['mr_id']:
                project_result[project_name]['mr_id']['good_commit_count'] = dict()
            if 'commit_count' not in project_result[project_name]['mr_id']:
                project_result[project_name]['mr_id']['commit_count'] = dict()

            comment_info = request_comment_info(
                request_url=project_id_prefix_map[project_id] + format_url_comment_subfix,
                project_id=project_id,
                merge_request_id=mr_id)
            mr_comment_count = analyze_comment_info(comment_info)
            project_result[project_name]['mr_id']['mr_comment_count'][str(mr_id)] = mr_comment_count

            commit_info = request_commit_info(request_url=project_id_prefix_map[project_id] + format_url_commit_subfix,
                                              project_id=project_id,
                                              merge_request_id=mr_id)
            good_commit_count, commit_count = analyze_commit_info(commit_info)
            project_result[project_name]['mr_id']['good_commit_count'][str(mr_id)] = good_commit_count
            project_result[project_name]['mr_id']['commit_count'][str(mr_id)] = commit_count

            # 项目的commit count数，判断此项目是否需要输出报告时用
            project_result[project_name]['commit_count'] += commit_count

            # print(
            #     "mr_id: " + str(mr_id) + ", mr_comment_count: " + str(mr_comment_count) + ", good_commit_count: " + str(
            #         good_commit_count))

        except BaseException as e:
            print("analyze_merge_request_info ex: ", str(e), repr(e))

    return list_len


# 获取merge request的comment信息
def request_comment_info(request_url, project_id, merge_request_id):
    url = request_url % (project_id, merge_request_id)
    # print("=================================url: ", url)

    try:
        r = requests.get(url=url, headers=project_id_token_map[project_id],
                         params={'order_by': 'created_at', 'sort': 'asc'},
                         timeout=3000)
        # print(r.text)
    except BaseException as e:
        print("request_comment_info ex: ", e)
    else:
        # print(r.text)
        return r


# 分析merge request的comment信息
def analyze_comment_info(comment_info):
    # print("=================================comment_info: ", comment_info)
    json_data = json.loads(comment_info.text)
    list_len = len(json_data)
    # print(list_len)

    non_system_note_count = 0
    for index in range(list_len):
        ele = json_data[index]
        # print(json.dumps(ele))

        if ele['system'] == False:
            non_system_note_count += 1

    return non_system_note_count


# 获取merge request的commit信息
def request_commit_info(request_url, project_id, merge_request_id):
    url = request_url % (project_id, merge_request_id)
    # print("=================================url: ", url)

    try:
        r = requests.get(url=url, headers=project_id_token_map[project_id],
                         timeout=3000)
        # print(r.text)
    except BaseException as e:
        print("request_commit_info ex: ", e)
    else:
        # print(r.text)
        return r


# 分析merge request的comment信息
def analyze_commit_info(commit_info):
    # print("=================================commit_info: ", commit_info)
    json_data = json.loads(commit_info.text)
    list_len = len(json_data)
    # print(list_len)

    good_commit_count = 0
    for index in range(list_len):
        ele = json_data[index]
        # print(json.dumps(ele))

        if len(ele['title']) > 5:
            good_commit_count += 1

    return good_commit_count, list_len


# 获取sonar的bugs信息
def request_sonar_bugs_info(request_url, project_key):
    url = request_url
    # print("=================================url: ", url)

    try:
        r = requests.get(url=url, headers=headers_auth,
                         params={'projectKeys': project_name_group_map[project_key] + ':' + project_key,
                                 'metricKeys': 'bugs,vulnerabilities,code_smells'},
                         timeout=3000)
        # print(r.text)
    except BaseException as e:
        print("request_sonar_bugs_info ex: ", e)
    else:
        # print(r.text)
        return r


# 分析sonar的bugs信息
def analyze_sonar_bugs_info(bugs_info):
    # print("=================================bugs_info: ", bugs_info)
    json_data = json.loads(bugs_info.text)

    bugs_count = 0
    vulnerabilities_count = 0

    metric_info = json_data['measures']
    list_len = len(metric_info)
    # print(list_len)
    if list_len == 0:
        # 表示并未在sonar上有报告，不合规
        return "不规范", "不规范"

    for index in range(list_len):
        ele = metric_info[index]
        # print(json.dumps(ele))

        if ele['metric'] == 'bugs':
            bugs_count = ele['value']
        elif ele['metric'] == 'vulnerabilities':
            vulnerabilities_count = ele['value']

    return str(bugs_count), str(vulnerabilities_count)


# 获取project的repository commit信息
def request_repository_commit(request_url, project_id):
    url = request_url % (project_id)
    # print("=================================url: ", url)

    try:
        r = requests.get(url=url, headers=project_id_token_map[project_id],
                         timeout=3000)
        # print(r.text)
    except BaseException as e:
        print("request_repository_commit ex: ", e)
    else:
        # print(r.text)
        return r


# 分析project的repository commit信息, 返回此项目是否活跃
def analyze_repository_commit(repository_commit, commit_timestamp_threshold):
    # print("=================================repository_commit: ", repository_commit)
    json_data = json.loads(repository_commit.text)
    list_len = len(json_data)
    # print(list_len)

    if list_len < commit_threshold:
        return False

    ele = json_data[commit_threshold - 1]
    # print(json.dumps(ele))
    created_at = ele['created_at']

    st = datetime.datetime.strptime(created_at[0:19], '%Y-%m-%dT%H:%M:%S')
    created_at_timestamp = int(time.mktime(st.timetuple()))
    # print(created_at_timestamp)

    return created_at_timestamp >= commit_timestamp_threshold


# 结果输出
def output_result(filepath, t):
    f = open(filepath, "w")
    f.write("[项目检查时间窗口:\t" + t.isoformat() + ' ~ ' + datetime.datetime.now().isoformat() + ']\n\n')

    i = 1
    for project_name in project_result:

        if project_result[project_name]['is_active']:
            f.write("*************  (" + str(i) + ") " + project_name_group_map[
                project_name] + ':' + project_name + " 项目规范性结果：\n")
            print("*************  (" + str(i) + ") " + project_name_group_map[
                project_name] + ':' + project_name + " 项目规范性结果：\n")

        # 不活跃的项目不输出
        if project_result[project_name]['is_active'] == False:
            # f.write("项目不活跃，忽略\n\n\n")
            continue

        i += 1

        # if project_result[project_name]['commit_count'] <= 5:
        #     f.write("项目不活跃, " + "commit数： " + str(project_result[project_name]['commit_count']) + " ，忽略\n\n\n")
        #     continue

        f.write("\n1. CI Pipeline规范性：\n")
        print("\n1. CI Pipeline规范性：\n")
        f.write('pipeline: \t' + ' ' + project_result[project_name]['pipeline'] + '\n')
        print('pipeline: \t' + ' ' + project_result[project_name]['pipeline'] + '\n')
        f.write("\n2. Sonar规范性：\n")
        print("\n2. Sonar规范性：\n")
        f.write('bug数:\t' + project_result[project_name]['sonar']['bugs_count'] + '\t' + '缺陷数:\t' +
                project_result[project_name]['sonar']['vulnerabilities_count'] + '\n')
        print('bug数:\t' + project_result[project_name]['sonar']['bugs_count'] + '\t' + '缺陷数:\t' +
              project_result[project_name]['sonar']['vulnerabilities_count'] + '\n')
        if project_result[project_name]['sonar']['bugs_count'] == -1:
            f.write("[!!!此项目未发现sonar报告!!!]\n")
            print("[!!!此项目未发现sonar报告!!!]\n")

        f.write("\n3. Merge Request规范性：\n")
        print("\n3. Merge Request规范性：\n")
        f.write('Merge Request总数:\t' + ' ' + str(project_result[project_name]['mr_count']) + '\n')
        print('Merge Request总数:\t' + ' ' + str(project_result[project_name]['mr_count']) + '\n')
        total_mr_comment_count = 0
        total_commit_count = 0
        total_good_commit_count = 0

        if 'mr_id' in project_result[project_name] and 'mr_comment_count' in project_result[project_name]['mr_id']:
            mr_id_result = project_result[project_name]['mr_id']['mr_comment_count']
            for mr_id in mr_id_result:
                # print("output mr_id: " + str(mr_id))
                try:
                    # f.write("Merge Request id: " + str(mr_id) + '\t' + '此Merge Request的评论数:' + ' ' + str(
                    #     project_result[project_name]['mr_id']['mr_comment_count'][
                    #         str(mr_id)]) + '\t' + '此Merge Request的commit次数:' + ' ' + str(
                    #     project_result[project_name]['mr_id']['commit_count'][
                    #         str(mr_id)]) + '\t' + '\t此Merge Request的合规commit次数:' + '\t' + str(
                    #     project_result[project_name]['mr_id']['good_commit_count'][str(mr_id)]) + '\n')

                    total_mr_comment_count += project_result[project_name]['mr_id']['mr_comment_count'][str(mr_id)]
                    total_commit_count += project_result[project_name]['mr_id']['commit_count'][str(mr_id)]
                    total_good_commit_count += project_result[project_name]['mr_id']['good_commit_count'][str(mr_id)]
                except BaseException as e:
                    print("output ex: ", e)
            f.write(
                "总评论数: " + str(
                    total_mr_comment_count) + comment_condition(total_mr_comment_count) + "\t总提交数: " + str(
                    total_commit_count) + commit_condition(total_commit_count) + "\t总合规提交数: " + str(
                    total_good_commit_count) + good_comment_condition(total_good_commit_count) + "\t提交信息合规率: " + str(
                    round(0 if total_commit_count == 0 else (total_good_commit_count / total_commit_count) * 100,
                          2)) + '%' + "\n")
            print("总评论数: " + str(
                total_mr_comment_count) + comment_condition(total_mr_comment_count) + "\t总提交数: " + str(
                total_commit_count) + commit_condition(total_commit_count) + "\t总合规提交数: " + str(
                total_good_commit_count) + good_comment_condition(total_good_commit_count) + "\t提交信息合规率: " + str(
                round(0 if total_commit_count == 0 else (total_good_commit_count / total_commit_count) * 100,
                      2)) + '%' + "\n")
        else:
            f.write("[!!!此项目未发现Merge Request!!!]")
            print("[!!!此项目未发现Merge Request!!!]")

        f.write("\n\n")
    f.close()
    return


# 判断条件
def comment_condition(total_mr_comment_count):
    return " [!!!总评论数过少!!!]" if (total_mr_comment_count <= 3) else ""


# 判断条件
def commit_condition(total_commit_count):
    return " [!!!总提交数过少!!!]" if total_commit_count <= 3 else ""


# 判断条件
def good_comment_condition(total_good_commit_count):
    return " [!!!总合规提交数过少!!!]" if total_good_commit_count <= 3 else ""


if __name__ == '__main__':
    args = parser.parse_args()
    # print(args.project)
    project_id = int(args.projectId)
    project_name = args.projectName
    group_id = int(args.groupId)
    gitlab_type = args.gitlabType
    gitlab_token = args.gitlabToken

    t = datetime.datetime.now() - datetime.timedelta(days=30)
    # print(t.isoformat())

    t_for_commit_threshold = datetime.datetime.now() - datetime.timedelta(days=30)
    # print(t_for_commit_threshold.isoformat())

    # 判断项目是否活跃的timestamp阈值,规则是截止到t_for_commit_threshold_timestamp内的repository commit的个数不少于k个
    t_for_commit_threshold_timestamp = int(t_for_commit_threshold.timestamp())

    # gitn group
    if gitlab_type == 'git_n':
        headers = {'PRIVATE-TOKEN': gitlab_token}
        result0_0 = request_group_project_info(request_url=format_url_prefix + format_url_group_project,
                                               group_id=group_id, headers=headers)
        analyze_group_project_info(result0_0, headers, format_url_prefix)
    elif gitlab_type == 'micode':
        # micode group
        headers_micode = {'PRIVATE-TOKEN': gitlab_token}
        result0_1 = request_group_project_info(request_url=format_url_micode_prefix + format_url_group_project,
                                               group_id=group_id, headers=headers_micode)
        analyze_group_project_info(result0_1, headers_micode, format_url_micode_prefix)
    else:
        print("gitType error!")
        exit("gitType error!")

    project_key = project_name
    # print("project_key: " + str(project_key) + ", project_id: " + str(
    #     project_id) + ", project_name: " +
    #       project_name)

    # calc pipeline
    result1 = request_pipeline_info(request_url=project_id_prefix_map[project_id] + format_url_pipeline_subfix,
                                    project_id=project_id)
    # print(analyze_pipeline_info(result1, stage_set=set()))
    project_result[project_name] = dict()
    pipeline_result = analyze_pipeline_info(result1, stage_set=set())
    project_result[project_name]['pipeline'] = '规范' if pipeline_result else '不规范'

    # calc merge request
    mr_count = 0
    project_result[project_name]['commit_count'] = 0

    for page_no in range(1, 20):
        # print("page_no: ", page_no)
        result2 = request_merge_request_info(
            request_url=project_id_prefix_map[project_id] + format_url_mr_subfix,
            project_id=project_id,
            page_no=page_no,
            created_after=t.isoformat())
        list_len = analyze_merge_request_info(result2, project_name, project_id)
        mr_count += list_len

        if list_len == 0:
            break
    # print("mr_count: ", mr_count)
    project_result[project_name]['mr_count'] = mr_count

    # calc sonar
    result4 = request_sonar_bugs_info(request_url=format_sonar_url_prefix + format_sonar_url_subfix,
                                      project_key=project_key)
    # print(analyze_sonar_bugs_info(result4))
    bugs_count, vulnerabilities_count = analyze_sonar_bugs_info(result4)

    project_result[project_name]['sonar'] = dict()
    project_result[project_name]['sonar']['bugs_count'] = bugs_count
    project_result[project_name]['sonar']['vulnerabilities_count'] = vulnerabilities_count

    # calc active project
    result5 = request_repository_commit(
        request_url=project_id_prefix_map[project_id] + format_url_repository_commit, project_id=project_id)
    is_active_project = analyze_repository_commit(repository_commit=result5,
                                                  commit_timestamp_threshold=t_for_commit_threshold_timestamp)

    project_result[project_name]['is_active'] = dict()
    project_result[project_name]['is_active'] = is_active_project

    print("#################################")
    # print(project_result)
    # output_result('ci_result.txt', t)
